package com.example.hos_samples.di;

import com.example.hos_samples.components.Constants;
import com.example.hos_samples.components.Counter;
import com.example.hos_samples.components.dynameic.DynScoped;
import com.example.hos_samples.components.dynameic.DynSingle;
import com.example.hos_samples.components.main.DumbServiceImpl;
import com.example.hos_samples.components.main.RandomId;
import com.example.hos_samples.components.main.Service;
import com.example.hos_samples.components.main.ServiceImpl;
import com.example.hos_samples.components.mvp.FactoryPresenter;
import com.example.hos_samples.components.mvp.ScopedPresenter;
import com.example.hos_samples.components.scope.Session;
import com.example.hos_samples.components.scope.SessionAbility;
import com.example.hos_samples.mvp.MVPAbility;
import com.example.hos_samples.scoped.ScopedAbilityA;
import org.koin.core.definition.BeanDefinition;
import org.koin.core.definition.Definition;
import org.koin.core.definition.OnCloseCallback;
import org.koin.core.error.DefinitionOverrideException;
import org.koin.core.module.Module;
import org.koin.core.parameter.DefinitionParameters;
import org.koin.core.qualifier.Qualifier;
import org.koin.core.scope.Scope;
import org.koin.dsl.DefinitionBinding;
import org.koin.dsl.ModuleDeclaration;
import org.koin.dsl.ScopeDSL;
import java.util.Arrays;
import java.util.List;

public class AppModule {

    public static final Module appModule = org.koin.dsl.Module.module(new ModuleDeclaration() {
        @Override
        public void invoke(Module module) {
            try {
                module.single(Service.class, new Definition<Service>() {
                    @Override
                    public ServiceImpl invoke(Scope scope, DefinitionParameters parameters) {
                        return new ServiceImpl();
                    }
                });

                module.single(Service.class, Qualifier.named("dumb"), new Definition<Service>() {
                    @Override
                    public DumbServiceImpl invoke(Scope scope, DefinitionParameters parameters) {
                        return new DumbServiceImpl();
                    }
                });

                module.factory(RandomId.class, new Definition<RandomId>() {
                    @Override
                    public RandomId invoke(Scope scope, DefinitionParameters parameters) {
                        return new RandomId();
                    }
                });

            } catch (DefinitionOverrideException e) {
                e.printStackTrace();
            }
        }
    });

    public static final Module mvpModule = org.koin.dsl.Module.module(new ModuleDeclaration() {
        @Override
        public void invoke(Module module) {
            try {
                module.factory(FactoryPresenter.class, new Definition<FactoryPresenter>() {
                    @Override
                    public FactoryPresenter invoke(Scope scope, DefinitionParameters parameters) {
                        try {
                            String id = parameters.component1(String.class);
                            return new FactoryPresenter(id, scope.get(Service.class));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return null;
                    }
                });

                module.scope(MVPAbility.class, new Module.ScopeSet() {
                    @Override
                    public void invoke(ScopeDSL scopeDSL) {
                        try {
                            scopeDSL.scoped(ScopedPresenter.class,
                                    new Definition<ScopedPresenter>() {
                                        @Override
                                        public ScopedPresenter invoke(Scope scope,
                                         DefinitionParameters parameters) {
                                            try {
                                                String id = parameters.component1(String.class);
                                                return new ScopedPresenter(id,
                                                        scope.get(Service.class));
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                            }
                                            return null;
                                        }
                                    });
                        } catch (DefinitionOverrideException e) {
                            e.printStackTrace();
                        }
                    }
                });

            } catch (DefinitionOverrideException e) {
                e.printStackTrace();
            }
        }
    });

    public static final Module scopeModule = org.koin.dsl.Module.module(new ModuleDeclaration() {
        @Override
        public void invoke(Module module) {
            module.scope(Qualifier.named(Constants.SCOPE_ID), new Module.ScopeSet() {
                @Override
                public void invoke(ScopeDSL scopeDSL) {
                    try {
                        BeanDefinition<Session> scoped = scopeDSL.scoped(Session.class,
                                Qualifier.named(Constants.SCOPE_SESSION),
                                false,
                                new Definition<Session>() {
                                    @Override
                                    public Session invoke(Scope scope,
                                                          DefinitionParameters parameters) {
                                        return new Session();
                                    }
                                });

                        DefinitionBinding.onClose(scoped, new OnCloseCallback<Session>() {
                            @Override
                            public void invoke(Session session) {
                                Counter.setReleased(Counter.getReleased() + 1);
                                System.out.println("Scoped -SCOPE_SESSION- release = "
                                        + Counter.getReleased());
                            }
                        });

                    } catch (DefinitionOverrideException e) {
                        e.printStackTrace();
                    }
                }
            });

            module.scope(ScopedAbilityA.class, new Module.ScopeSet() {
                @Override
                public void invoke(ScopeDSL scopeDSL) {
                    try {
                        scopeDSL.scoped(Session.class, new Definition<Session>() {
                            @Override
                            public Session invoke(Scope scope, DefinitionParameters parameters) {
                                return new Session();
                            }
                        });

                        scopeDSL.scoped(SessionAbility.class, new Definition<SessionAbility>() {
                            @Override
                            public SessionAbility invoke(Scope scope,
                                                         DefinitionParameters parameters) {
                                try {
                                    return new SessionAbility(parameters.get(ScopedAbilityA.class));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                return null;
                            }
                        });

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    });

    public static final Module dynamicModule = org.koin.dsl.Module.module(new ModuleDeclaration() {
        @Override
        public void invoke(Module module) {
            try {
                module.single(DynSingle.class, new Definition<DynSingle>() {
                    @Override
                    public DynSingle invoke(Scope scope, DefinitionParameters parameters) {
                        return new DynSingle();
                    }
                });

                module.scope(Qualifier.named("dynamic_scope"), new Module.ScopeSet() {
                    @Override
                    public void invoke(ScopeDSL scopeDSL) {
                        try {
                            scopeDSL.scoped(DynScoped.class, new Definition<DynScoped>() {
                                @Override
                                public DynScoped invoke(Scope scope,
                                                        DefinitionParameters parameters) {
                                    return new DynScoped();
                                }
                            });
                        } catch (DefinitionOverrideException e) {
                            e.printStackTrace();
                        }
                    }
                });

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });

    public static final List<Module> allModules = Arrays.asList(appModule,
            mvpModule,
            scopeModule,
            dynamicModule);
}
